//
// Created by 27531 on 2022/2/14.
//
#include <cstdio>
#include <cstdlib>
#include "VideoChannel.h"

VideoChannel::VideoChannel() {
    LOGI("============= VideoChannel::VideoChannel =============");

}
VideoChannel::~VideoChannel() {
    LOGI("============= VideoChannel::~VideoChannel =============");

}
void* loopCodecPacket(void *data) {
    LOGI("============= loopCodecPacket =============");
    while (true) {
        LOGI("============= loop =============");
    }
    pthread_exit(NULL);
}
void VideoChannel::init() {
    LOGI("============= VideoChannel::init =============");
//    pthread_create(&mVideo_codec_thread, NULL, loopCodecPacket, this);
}

void VideoChannel::setVideoEncInfo(int width, int height, int fps, int bitrate) {
    LOGI("============= VideoChannel::setVideoEncInfo width = %d, height = %d, fps = %d, bitrate = %d =============", width, height, fps, bitrate);
    mWidth = width;
    mHeight = height;
    mFps = fps;
    mBitrate = bitrate;

    mYSize = width * height;
    mUVSize = mYSize / 2;

    if (mVideoCodec) {
        x264_encoder_close(mVideoCodec);
        mVideoCodec = NULL;
    }

    //    定义参数
    x264_param_t param;
    //    参数赋值   x264  麻烦  编码器 速度   直播  越快 1  越慢2
    x264_param_default_preset(&param, "ultrafast", "zerolatency");
    //编码等级
    param.i_level_idc = 32;
    //    选取显示格式 planar模式，先存储U，再存储V
    param.i_csp = X264_CSP_I420;
    param.i_width = width;
    param.i_height = height;
    //    B帧
    param.i_bframe = 0;
    //折中    cpu   突发情况   ABR 平均
    param.rc.i_rc_method = X264_RC_ABR;
    //k为单位
    param.rc.i_bitrate = bitrate / 1024;
    //帧率   1s/25帧     40ms  视频 编码      帧时间 ms存储  us   s
    param.i_fps_num = fps;
    //    帧率 时间  分子  分母
    param.i_fps_den = 1;
    //    分母
    param.i_timebase_den = param.i_fps_num;
    //    分子
    param.i_timebase_num = param.i_fps_den;

    //单位 分子/分母    发热  --
    //用fps而不是时间戳来计算帧间距离
    param.b_vfr_input = 0;
    //I帧间隔     2s  15*2
    param.i_keyint_max = fps * 2;

    // 是否复制sps和pps放在每个关键帧的前面 该参数设置是让每个关键帧(I帧)都附带sps/pps。
    param.b_repeat_headers = 1;
    //    sps  pps  赋值及裙楼
    //多线程
    param.i_threads = 1;
    x264_param_apply_profile(&param, "baseline");
    //    打开编码器  宽 高一定是交换的
    mVideoCodec = x264_encoder_open(&param);
    //容器
    mPic_in = new x264_picture_t;
    mPic_out = new x264_picture_t;
    //设置初始化大小  容器大小就确定的
    x264_picture_alloc(mPic_in, X264_CSP_I420, width, height);
    x264_picture_init(mPic_out);
}

void VideoChannel::encodeData(int8_t *y, int8_t *u, int8_t *v, int ylength, int ulength, int vlength) {
    LOGI("========== VideoChannel::encodeData start ylength = %d, ulength = %d, vlength = %d ==========", ylength, ulength, vlength );
//    pthread_mutex_lock(&mEncoder_mutex);
    //    容器   y的数据
    memcpy(mPic_in->img.plane[0], y, ylength);
    // *u 代表的数据是 uvuvuv...uvuv
    for (int i = 0; i < ulength; i+=2) {
        mPic_in->img.plane[1][i] = u[i];
    }
    // *v代表的数据是 vuvuvu...vuvu
    for (int i = 0; i < vlength; i+=2) {
        mPic_in->img.plane[2][i] = v[i];
    }

    //编码出了几个 nalu （暂时理解为帧）  1   pi_nal  1  永远是1
    int pi_nal;
    //     编码出了几帧
    //编码出的数据 H264
    x264_nal_t *pp_nals;
//    //编码出的参数  BufferInfo
    //关键的一句话   yuv  数据 转化 H264
    x264_encoder_encode(mVideoCodec, &pp_nals, &pi_nal, mPic_in, mPic_out);
    LOGI("========== encodeData  pi_nal = %d ==========", pi_nal);

    mPic_in->i_pts++;
    mPic_out->i_pts++;
    for (int i = 0; i < pi_nal; ++i) {
        if (pp_nals[i].i_type == NAL_SPS) {
            LOGI("encodeData 输出NAL_SPS, frame_count = %d", frame_count);
            ++frame_count;
        } else if (pp_nals[i].i_type == NAL_PPS) {
            LOGI("encodeData 输出NAL_PPS, frame_count = %d", frame_count);
            ++frame_count;
        } else {
            LOGI("encodeData 输出 %d, frame_count = %d", pp_nals[i].i_type, frame_count);
            ++frame_count;
        }
    }
//    pthread_mutex_unlock(&mEncoder_mutex);
}

void VideoChannel::close() {
    LOGI("============= VideoChannel::close =============");
    if (mPic_in != NULL){
        x264_picture_clean(mPic_in);
        mPic_in = NULL;
    }
    if (mPic_out != NULL){
        x264_picture_clean(mPic_out);
        mPic_out = NULL;
    }
    if (mVideoCodec != NULL){
        x264_encoder_close(mVideoCodec);
        mVideoCodec = NULL;
    }
}
